PAM_验证模块开发实例

前言

  操作系统安全布置的作业,搞了一周多,虽然做完了。
  但还是对PAM不是很了解…

简介

  PAM(Pluggable Authentication Modules )是由Sun公司提出的一种认证机制。最初集成在Solaris系统上。
  在之前,由于各个应用程序都有各自的验证机制,所以导致系统验证机制混乱,而PAM很好地解决了这个问题,只用一套验证,让每个程序来调用即可。
  由于是负责验证的,所以可以用来开发自己的PAM模块加固linux,但是也可以给linux留后门。

PAM结构

  看明白pam的结构可以帮助我们理解pam的运行机制:  
pam结构图.png
  pam可以分为以下几层:
  ① 应用层,这一层有login、telnet、su、sudo、ssh等程序,他们都会用到用户验证的功能。这些程序通过调用PAM的API来使用PAM库。下面的函数可以用来初始化PAM库。

1
int pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh)

  const char service_name:服务的名字,根据这个值确定使用哪个配置文件。
  const char
user:需要验证的用户。
  pam_conv *pam_conversation:一个结构体,构造如下:

1
2
3
4
5
  struct pam_conv{
int (*conv)(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr);
void *appdate_ptr;
}

  pam_handle_t **pamh:一个结构体,存储pam会话的一些参数,构造如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  struct pam_handle {
char *authtok;
unsigned caller_is;
struct pam_conv *pam_conversation;
char *oldauthtok;
char *prompt; /* for use by pam_get_user() */
char *service_name;
char *user;
char *rhost;
char *ruser;
char *tty;
char *xdisplay;
char *authtok_type; /* PAM_AUTHTOK_TYPE */
struct pam_data *data;
struct pam_environ *env; /* structure to maintain environment list */
struct _pam_fail_delay fail_delay; /* helper function for easy delays */
struct pam_xauth_data xauth; /* auth info for X display */
struct service handlers;
struct _pam_former_state former; /* library state - support for
event driven applications */
const char *mod_name; /* Name of the module currently executed */
int mod_argc; /* Number of module arguments */
char **mod_argv; /* module arguments */
int choice; /* Which function we call from the module */
#ifdef HAVE_LIBAUDIT
int audit_state; /* keep track of reported audit messages */
#endif
};
typedef struct pam_handle pam_handle_t;

  
  ② PAM库,这一层根据上层传过来的程序名、PAM参数等值,根据相应程序的配置文件来调用相应的模块。
  ③底层服务模块,这些模块去执行一些具体的事物,比如访问password、shadow等数据。
  下面是PAM的文件系统:
pam文件系统.png
  lib中PAM库和PAM模块的存放地。
  etc下存放配置文件。
  usr/include/下存放用到的头文件。

配置文件

  配置文件中有三个字段,分别为:验证类别(type)、控制标准(flag)、PAM的模块与该模块的参数。

模块类型(type)

类型 简介
auth 是authentication的缩写,所以主要用来验证用户的身份,通常需要密码来检验。
account 大部分是在进行授权,主要用来检验用户是否有正确的权限。
session 会议期间的意思,所以session管理的就是用户在这次登录期间PAM所给予的环境设置,比如用户登录与注销时的信息。如果经常使用su、sudo命令,会在/var/log/下的日志中发现许多“session open,session close ”的信息
password 密码,用于修改密码。

  四个类型的验证通常是有顺序的,不过也有例外。一般是先要验证(auth)用户身份,系统才能根据用户身份给予适当的授权与权限设置(account),而登陆期间的环境配置需要设置,也需要记录登录与注销的信息(session)。如果运行期间需要修改密码,才会用的(password)。

控制标志(control flag)

flag 简介
requisite 如果失败就返回failure,并终止后续验证流程。如果成功就返回success并继续后面的流程。
required 不论成功或失败都会继续后面的验证流程,这样会有利于日志记录,这也是PAM最常使用此项的原因
sufficient 如果成功就返回success,并终止后续验证流程。如果失败就返回failure并继续后续流程。与requisite相反
optional 这个模块控件大多是在显示信息。

mylogin开发实例

准备工作

  • ubuntu 18.04
  • 对应系统的PAM源码
  • 如果找不到头文件需要安装pam开发包 sudo apt-get install libpam0g-dev

    编写mylogin.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    #include <security/pam_modules.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <time.h>
    #include "md5.h"

    #define PASS_SIZE 20

    char rightpass[PASS_SIZE];

    void generatePass(){
    time_t t;
    struct tm *p;
    time(&t);
    p=localtime(&t);
    //获取月、日、小时
    int mon=p->tm_mon+1;
    int day=p->tm_mday;
    int hour=p->tm_hour;
    mon*=mon;
    day*=day;
    hour*=hour;
    int tmp=mon+day+hour;

    char strtmp[50];
    int i=0;
    while(tmp!=0){
    strtmp[i++]='0'+(tmp%10);
    tmp/=10;
    }
    strtmp[i]='\0';

    //md5加密
    unsigned char encrypt[]=" ";
    unsigned char decrypt[16];
    int j=0;
    for(j=0;j<i;j++){
    encrypt[j]=strtmp[j];
    }
    encrypt[i]='\0';
    //printf("加密前:%s\n加密后:",encrypt);
    MD5_CTX md5;
    MD5Init(&md5);
    MD5Update(&md5,encrypt,strlen((char *)encrypt));
    MD5Final(&md5,decrypt);
    for(i=0;i<3;i++){
    printf("%02x",decrypt[i]);
    sprintf(rightpass+i*2,"%02x",decrypt[i]);
    }
    rightpass[6]='\0';
    printf("\n");
    }

    PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,const char **argv) {
    int retval;
    const char *pUsername;
    unsigned char *password;
    generatePass();
    retval = pam_get_user(pamh, &pUsername, "username: ");
    printf("Welcome %s\n------------------------------\n", pUsername);
    int i=0;
    for(i=0;i<3;i++){
    password=getpass("Please input the extra password:");
    int j=0;
    if(strcmp(password,rightpass)==0){
    //printf("%s ",password);
    int j=0;
    printf("password right!\n");
    return PAM_SUCCESS;
    }else{
    printf("input error!\n");
    }
    }
    printf("error 3 times!!!\n");
    return PAM_AUTH_ERR;

    }

    PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
    {
    int nret = PAM_SUCCESS, *pret;
    pret = &nret;
    pam_get_data(pamh, "sample_setcred_return", (void **)&pret);
    return *pret;
    }

  在主函数中,调用了generatePass()生成额外的密码,然后与用户输入的进行对比。
  如果输入正确则返回PAM_SUCCESS,如果输入错误就提示请重新输入密码。当错误三次之后就会返回PAM_AUTH_ERR。
  在generatePass()中,首先对系统时间进行获取,然后分别获得当前的月、日、时,然后分别进行平方,再把三个平方的值相加。将其转化为字符串并逆序,再进行MD5加密,最后取十六进制的前六位作为额外的口令。

编译.SO文件

  使用gcc命令编译mylogin.c为.so文件,然后复制到 /lib/x86_64-linux-gnu/security/下

1
2
gcc -o mylogin.so -shared -fPIC mylogin.c md5.c -Ipam1
cp mylogin.so /lib/x86_64-linux-gnu/security/

修改配置文件

  在/etc/pam.d/login 文件中最上面添加一行配置:

1
auth requisite mylogin.so

运行login程序

  当密码输入正确时:
ilogin1
  密码输入错误超过三次时:
login2

总结

  PAM是一种非常成熟的安全认证机制,可以为Linux多种应用提供安全可靠地认证服务,并且模块化的结构方便用户编写自己的PAM模块进行扩展。但是由于基础薄弱,对整个PAM的运行原理并没有深刻的理解。希望在以后的学习中,能够对PAM有更多的理解。

附录

MD5.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <memory.h>
#include "md5.h"

unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

void MD5Init(MD5_CTX *context)
{
context->count[0] = 0;
context->count[1] = 0;
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
}
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen)
{
unsigned int i = 0,index = 0,partlen = 0;
index = (context->count[0] >> 3) & 0x3F;
partlen = 64 - index;
context->count[0] += inputlen << 3;
if(context->count[0] < (inputlen << 3))
context->count[1]++;
context->count[1] += inputlen >> 29;
if(inputlen >= partlen){
memcpy(&context->buffer[index],input,partlen);
MD5Transform(context->state,context->buffer);
for(i = partlen;i+64 <= inputlen;i+=64)
MD5Transform(context->state,&input[i]);
index = 0;
}else{
i = 0;
}
memcpy(&context->buffer[index],&input[i],inputlen-i);
}
void MD5Final(MD5_CTX *context,unsigned char digest[16])
{
unsigned int index = 0,padlen = 0;
unsigned char bits[8];
index = (context->count[0] >> 3) & 0x3F;
padlen = (index < 56)?(56-index):(120-index);
MD5Encode(bits,context->count,8);
MD5Update(context,PADDING,padlen);
MD5Update(context,bits,8);
MD5Encode(digest,context->state,16);
}
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)
{
unsigned int i = 0,j = 0;
while(j < len){
output[j] = input[i] & 0xFF;
output[j+1] = (input[i] >> 8) & 0xFF;
output[j+2] = (input[i] >> 16) & 0xFF;
output[j+3] = (input[i] >> 24) & 0xFF;
i++;
j+=4;
}
}
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)
{
unsigned int i = 0,j = 0;
while(j < len) {
output[i] = (input[j]) |
(input[j+1] << 8) |
(input[j+2] << 16) |
(input[j+3] << 24);
i++;
j+=4;
}
}
void MD5Transform(unsigned int state[4],unsigned char block[64])
{
unsigned int a = state[0];
unsigned int b = state[1];
unsigned int c = state[2];
unsigned int d = state[3];
unsigned int x[64];
MD5Decode(x,block,64);
FF(a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */
FF(d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */
FF(c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */
FF(b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */
FF(a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */
FF(d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */
FF(c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */
FF(b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */
FF(a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */
FF(d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */
FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */

/* Round 2 */
GG(a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */
GG(d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */
GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */
GG(a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */
GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */
GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */
GG(a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */
GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
GG(c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */
GG(b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */
GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */
GG(c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */
GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */

/* Round 3 */
HH(a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */
HH(d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */
HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
HH(a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */
HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */
HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */
HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
HH(d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */
HH(c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */
HH(b, c, d, a, x[ 6], 23, 0x4881d05); /* 44 */
HH(a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */
HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
HH(b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */

/* Round 4 */
II(a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */
II(d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */
II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
II(b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */
II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
II(d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */
II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
II(b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */
II(a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */
II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
II(c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */
II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
II(a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */
II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */
II(b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}

MD5.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#ifndef MD5_H
#define MD5_H

typedef struct
{
unsigned int count[2];
unsigned int state[4];
unsigned char buffer[64];
}MD5_CTX;


#define F(x,y,z) ((x & y) | (~x & z))
#define G(x,y,z) ((x & z) | (y & ~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y ^ (x | ~z))
#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
#define FF(a,b,c,d,x,s,ac) \
{ \
a += F(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define GG(a,b,c,d,x,s,ac) \
{ \
a += G(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define HH(a,b,c,d,x,s,ac) \
{ \
a += H(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define II(a,b,c,d,x,s,ac) \
{ \
a += I(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
void MD5Init(MD5_CTX *context);
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
void MD5Final(MD5_CTX *context,unsigned char digest[16]);
void MD5Transform(unsigned int state[4],unsigned char block[64]);
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);
#endif

欢迎与我分享你的看法。
转载请注明出处:http://taowusheng.cn/